
#include <devmgr.h>
#include <linkedlist.h>
#include <kmalloc.h>
#include <debug.h>
#include <idt.h>
#include <mp.h>
#include <string.h>
#include <pic.h>
#include <io.h>

static linkedlist_t *devices = NULL;
static linkedlist_t **isr_list;
static byte intmode;
static dword curr_handle;

void devmgr_isr(int vector, int code);

/**
 * Initialize the Device Manager
 * 
 * returns - error
 */
int init_devmgr(void)
{
	int ret = 0;
	
	curr_handle = 1;
	
	intmode = mp_get_intmode();
	
	if (intmode == MP_INTMODE_PIC)
	{
		// install 16 isr's for PIC irqs
		isr_list = (linkedlist_t**)kmalloc(sizeof(linkedlist_t*)*16);
		if (isr_list == NULL)
		{
			debugf("[DEVMGR] Could not allocate ISR structures\n");
			return DEV_ERR_NOALLOC;
		}
		
		int i;
		for (i = 0; i < 16; i++)
		{
			idt_set_isr(i + g_irqbase, devmgr_isr);
			isr_list[i] = NULL;
		}
	}
	else
	{
		// ...
	}
	
	return ret;
}

/**
 * Add a device to the Device Manager
 * 
 * device_t *dev - device structure to add
 * 
 * returns - error
 */
int devmgr_add_device(device_t *dev)
{
	int ret = 0;

	// do some checking...
	if ((dev->gint < 0) || ((intmode == MP_INTMODE_PIC) && (dev->gint > 15)))
	{
		return DEV_ERR_BADGINT;
	}
	if (dev->locator == NULL)
	{
		return DEV_ERR_BADLOCATOR;
	}
	if (dev->handle == 0)
	{
		return DEV_ERR_BADHANDLE;
	}
	
	linkedlist_t *node = (linkedlist_t*)kmalloc(sizeof(linkedlist_t));

	if (node != NULL)
	{
		node->node = dev;
		linkedlist_insert_before(node, devices);
		devices = node;
		
		if (dev->handler != NULL)
		{
			// create a new element for the list of isrs and add it to
			// the appropriate list
			linkedlist_t *isr = (linkedlist_t*)kmalloc(sizeof(linkedlist_t));
			if (isr == NULL)
			{
				debugf("[DEVMGR] Could not allocate ISR structure\n");
				return DEV_ERR_NOALLOC;
			}
			isr->node = dev->handler;
			isr->next = isr->prev = NULL;
			linkedlist_insert_before(isr, isr_list[dev->gint]);
			isr_list[dev->gint] = isr;
		}
	}
	else
	{
		debugf("[DEVMGR] Could not allocate device node\n");
		return DEV_ERR_NOALLOC;
	}
	
	return ret;
}

/**
 * Enumerate devices currently managed
 * 
 * f_device_enum - function which shall be called for each device in the manager
 * void **context - context information which shall be passed to the function callback
 * 
 * returns - error
 */
int enumerate_devices(f_device_enum enumerator, void **context)
{
	int ret = 0;
	linkedlist_t *node = devices;
	
	while (node != NULL)
	{
		ret = enumerator(node->node, context);
		node = node->next;
	}
	
	return ret;
}

/**
 * Create a unique handle
 * 
 * returns - unique handle
 */
dword devmgr_create_handle(void)
{
	return curr_handle++;
}

/**
 * Get a device structure by handle
 * 
 * dword handle - device handle
 * 
 * returns - pointer to device structure
 */
device_t * devmgr_get_device(dword handle)
{
	device_t *dev = NULL;
	device_t *tmp = NULL;
	linkedlist_t *node = devices;
	
	// search
	while ((node != NULL) && (dev == NULL))
	{
		tmp = (device_t*)node->node;
		
		if (tmp->handle == handle)
			dev = tmp;
		
		node = node->next;
	}
	
	return dev;
}

/**
 * Main entry point for all interrupts. Calls the appropriate device ISR if a
 * device is registered for this interrupt. When multiple devices have
 * registered for the same interrupt, this function calls them in order until
 * one of the ISRs indicates that it has handled the interrupt.
 * 
 * int vector - number if interrupt
 * int code - code, if any, for interrupt (not used for irqs)
 */
void devmgr_isr(int vector, int code)
{
	//debugf("[DEVMGR] int %d (0x%08x)\n", vector, isr_list[vector-g_irqbase]);
	
	int handled = 1;
	linkedlist_t *isr = isr_list[vector-g_irqbase];
	while ((isr != NULL) && (handled != 0))
	{
		handled = ((f_driver_handler)isr->node)();
		isr = isr->next;
	}
	
	if (intmode == MP_INTMODE_PIC)
	{
		// TODO: maybe move this to pic code, probably faster without func call
		// also, verify that this works for slave pic irqs
		outb(PIC_MASTER_CMD, PIC_EOI);
		if (vector >= g_irqbase + 8)
			outb(PIC_SLAVE_CMD, PIC_EOI);
	}
}
